package com.zp.qualitypractice.list;

import android.annotation.TargetApi;
import android.content.Context;
import android.os.Build;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.DecelerateInterpolator;
import android.widget.ListView;
import android.widget.Scroller;

import com.zp.qualitypractice.R;
import com.zp.qualitypractice.list.header.ListRefreshHeader;

/**
 * Created by izp on 2017/4/20.
 */

public class RefreshListView extends ListView {

    private Scroller mScroller;
    private ListRefreshHeader mHeader;
    private View mHeaderContent;


    private int mHeaderContentHeight;//mHeaderContent的高度
    private boolean mIsRefreshing = false;
    private boolean mIsClose;

    private float mLastY = -1;

    private OnRefreshListener mRefreshListener;



    public RefreshListView(Context context) {
        super(context);
        init(context);
    }

    public RefreshListView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context);
    }

    public RefreshListView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context);
    }

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public RefreshListView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        init(context);
    }

    private void init(Context context) {
        /*
        * Scroller是一个专门用于处理滚动效果的工具类，可能在大多数情况下，我们直接使用Scroller的场景并不多，
        * 但是很多大家所熟知的控件在内部都是使用Scroller来实现的，如ViewPager、ListView等。
        * */

        /*
        * 先撇开Scroller类不谈，其实任何一个控件都是可以滚动的，因为在View类当中有scrollTo()和scrollBy()这两个方法
        *
        * 那么它们之间有什么区别呢？简单点讲，scrollBy()方法是让View相对于当前的位置滚动某段距离，
        * 而scrollTo()方法则是让View相对于初始的位置滚动某段距离。
        * */

        /*
        * 相信大家已经把scrollTo()和scrollBy()这两个方法的区别搞清楚了，但是现在还有一个问题，
        * 从上图中大家也能看得出来，目前使用这两个方法完成的滚动效果是跳跃式的，没有任何平滑滚动的效果。
        * 没错，只靠scrollTo()和scrollBy()这两个方法是很难完成ViewPager这样的效果的，因此我们还需要借助另外一个关键性的工具，也就我们今天的主角Scroller。
        * */
        mScroller = new Scroller(context,new DecelerateInterpolator());//创建一个平滑移动实例，传入减速插值器
        mHeader = new ListRefreshHeader(context);
        mHeaderContent = mHeader.findViewById(R.id.header_content);

        addHeaderView(mHeader);//recyclerView没有header的时候怎么搞
        //        使用这个方法获取高度，测试没毛病
        mHeader.post(new Runnable() {
            @Override
            public void run() {
                mHeaderContentHeight = mHeaderContent.getHeight();
                mHeader.setContentHeight(mHeaderContentHeight);
            }
        });
    }

    public void finishRefresh() {
        if (mIsRefreshing){
            mIsRefreshing = false;
            resetHeaderHeight();
            mHeader.stopLoading();
        }
    }
    public void setRefreshListener(OnRefreshListener mRefreshListener) {
        this.mRefreshListener = mRefreshListener;
    }

    /**
     * @param distance 动态更新Header的高度
     */
    private void updateHeaderHeight(float distance) {
//        mHeaderViewContainer.setLayoutParams(lp);
        mHeader.setShowHeight((int)(distance+mHeader.getShowHeight()));
        if (!mIsRefreshing){
            if (mHeader.getShowHeight() > mHeaderContentHeight){//当前没有正在刷新，然后item比内容大
                mHeader.updateText("松开刷新");
            }else {
                mHeader.updateText("下拉刷新");
            }
        }
        setSelection(0);//把列表移动到0这个item
    }

    private void resetHeaderHeight() {
        int height = mHeader.getShowHeight();//获取item内部中用于显示内容的部分的高度
        if (height ==0){//如果item的内容部分没有高度，那么就直接返回
            return;
        }
        if (mIsRefreshing && height <= mHeaderContentHeight) {//如果正在刷新，且当前获取到的内容导读小于外部容器的高度，
            return;
        }

        int finalHeight = 0;
        // 如果当前是刷新中状态，且Header的展示高度要大于Header的真实高度，则滑动列表，完整展示Header，否则隐藏Header
        if (mIsRefreshing && height> mHeaderContentHeight){
            finalHeight = mHeaderContentHeight;
        }
        mIsClose =true;
        mScroller.startScroll(0,height,0,finalHeight-height,200);
//        invalidate();//可以试一试不重绘
        //重新绘制ListView
    }

    @Override
    public void computeScroll() {
        if (mScroller.computeScrollOffset()) {
            if (mIsClose) {
                mHeader.setShowHeight(mScroller.getCurrY());//把当前Y坐标赋值给当前的item高度，

            }
//            postInvalidate();
//            invalidate();
            //可以在子线程重刷新界面，这里用invalidate应该也是一样
//            后来测试发现这里用不用invalidate都一样，重绘List没有意义
        }
        super.computeScroll();
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
//        return super.onTouchEvent(ev);
        if (mLastY == -1) {
            mLastY = ev.getRawY();//上一次手指触摸的点的Y轴(整个屏幕位置)
        }

        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                mLastY = ev.getRawY();//获取触摸的整个屏幕的坐标的Y轴
                break;
            case MotionEvent.ACTION_MOVE:
                final float distanceY = ev.getRawY() - mLastY;
                mLastY = ev.getRawY();
                // 如果列表中第一个Item是可见的, 且Header的部分可见或者向下滑动，则动态设置Header高度
                if (getFirstVisiblePosition() == 0
                        && (mHeader.getShowHeight() > 0 || distanceY > 0)) {
                    updateHeaderHeight(distanceY * 0.8f);//header的放大距离小于是滑动距离
                    return true;//是消费事件还是拦截事件
                }
                break;
            default:
//                其他事件时，将上次触摸的Y轴位置重置
                mLastY = -1;
                // 用户松开手(这里应该就是其它事件)时候，如果列表第一个Item可以见
                if (getFirstVisiblePosition()==0){
                    if (mHeader.getShowHeight()  > mHeaderContentHeight){//应该是showHeight到大一定程度，而不仅仅是大于吧
                        mIsRefreshing = true;
                        mHeader.updateText("正在刷新");
                        mHeader.startLoading();
                        if (mRefreshListener != null) {
                            mRefreshListener.onRefresh();
                        }
                    }
                    // 根据当前情况重置Header高度
                    resetHeaderHeight();
                    return true;
                }
                break;
        }

        return super.onTouchEvent(ev);
    }
}
